package com.cms.bs.j2ee.aspect;

import com.cms.bs.app.model.User;
import com.cms.bs.config.Constants;
import com.cms.bs.config.SessionHelper;
import com.cms.bs.j2ee.annotation.Log;
import com.cms.bs.util.CommonUtil;
import com.cms.bs.util.IPUtil;
import com.cms.bs.util.JsonUtil;
import com.cms.bs.util.Log4jUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by jiang on 2016-11-12.
 */
@Aspect
@Component
public class LogAspect {

    private HttpServletRequest request;

    // 进程处理时间
    ThreadLocal<Long> time = new ThreadLocal<Long>();
    // 进程请求唯一标识
    ThreadLocal<String> tag = new ThreadLocal<String>();

    /**
     * Controller Aspect
     */
    @Pointcut("@annotation(com.cms.bs.j2ee.annotation.Log)")
    @PostConstruct
    public void log() {
    }

    /**
     * 执行前 通知
     *
     * @param joinPoint
     */
    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        map.put("key", "doBefore");

        // 设置进程信息
        time.set(System.currentTimeMillis());
        tag.set(CommonUtil.getRandomStr(8));
        map.put("id", tag.get());

        try {
            // 获得注解
            Log log = getAnnotation(joinPoint);
            if (log == null) {
                return;
            }

            // 获取方法名
            MethodSignature ms = (MethodSignature) joinPoint.getSignature();
            String methodName = ms.getMethod().getName();
            map.put("method", methodName);

            // 获取参数
            Object[] args = joinPoint.getArgs();
            if (args.length > 0) {
                StringBuffer sb = new StringBuffer("");
                for (Object arg : args) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }
                    sb.append(arg);
                }
                map.put("args", sb);
            }

            // 获取annotation描述
            String classType = joinPoint.getTarget().getClass().getName();
            Class<?> clazz = Class.forName(classType);
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Log.class) && method.getName().equals(methodName)) {
                    map.put("desription", log.description());
                    break;
                }
            }

            // 读取session中的用户
            User user = (User) SessionHelper.get(Constants.CURRENT_USER);
            if (user != null) {
                map.put("user", user.getName());
            }

            // 请求的IP
            String ip = IPUtil.getIpAddr(request);
            map.put("ip", ip);
        } catch (Exception e) {
            Log4jUtil.error(this.getClass().getName(), "doBefore deal exception.", e);
        }
        Log4jUtil.info(this.getClass().getName(), handleLog(map));
    }

    /**
     * 返回通知
     *
     * @param joinPoint
     */
    @After("log()")
    public void doAfter(JoinPoint joinPoint) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        map.put("key", "doAfter");
        map.put("id", tag.get());
        map.put("times", (System.currentTimeMillis() - time.get()) + "ms");

        Log4jUtil.info(this.getClass().getName(), handleLog(map));
    }

    /**
     * 返回值 增强处理 通知
     *
     * @param joinPoint
     * @param retVal
     */
    @AfterReturning(value = "log()", argNames = "joinPoint,retVal", returning = "retVal")
    public void doAfterRetuning(JoinPoint joinPoint, Object retVal) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        map.put("key", "doAfterRetuning");
        map.put("id", tag.get());
        map.put("result", JsonUtil.toJsonString(retVal));

        Log4jUtil.info(this.getClass().getName(), handleLog(map));
    }

    /**
     * 异常通知
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "log()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();
        map.put("key", "doAfterThrowing");
        map.put("id", tag.get());
        map.put("exception", e);

        Log4jUtil.info(this.getClass().getName(), handleLog(map));
    }

    /**
     * 获取 Annotation
     *
     * @param joinPoint
     * @return
     */
    private Log getAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    /**
     * 处理 日志
     *
     * @param map
     * @return
     */
    private String handleLog(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer("");
        for (String key : map.keySet()) {
            if (sb.length() > 0) {
                sb.append("; ");
            }
            sb.append(key).append(":").append("[").append(map.get(key)).append("]");
        }
        return sb.toString();
    }

}
